/**
 * 简易版编译器, 用来解析 Vue 文件
 * 它把 Vue 文件拆分成 template、script、style 三部分
 * template: 通过 updateTemplate() 函数更新 dom
 * script: 通过 setScript() 添加到 html 文件中
 * style: 通过 setStyle() 添加到 html 文件中
 */
import { nextTick } from "./core/util.js";
import { ref } from "./core/observer.js";
window.$ref = ref;

// 解析模版数据
export const APP_DATA = {
  vm: null,
  templateStr: "",
  scriptStr: "",
  styleStr: "",
  proxyMap: {},
};

// 读取 vue 组件内容并提取 template、script、style 对应的内容
const compilerVue = (param, vm) => {
  const getContent = (regex) => {
    const matches = regex.exec(param);
    if (matches && matches.length > 1) {
      return matches[1].trim();
    }
    return "";
  };
  const templateStr = getContent(/<template>([\s\S]+)<\/template>/);
  const scriptStr = getContent(/<script setup>([\s\S]+)<\/script>/);
  const styleStr = getContent(/<style>([\s\S]+)<\/style>/);
  // 给 script 中的 ref 方法添加变量参数, 让变量名和值一一对应
  const setRefFunc = (scriptStr) => {
    const regex = /const\s+(\w+)\s*=\s*ref\([^\)]*(?=\))/g;
    return scriptStr.replace(regex, ($0, $1) => {
      return `${$0}, '${$1}'`;
    });
  };
  return Object.assign(APP_DATA, {
    vm,
    templateStr,
    scriptStr: setRefFunc(scriptStr),
    styleStr,
  });
};

/**
 * 1. 提取 script 中的 import 导入代码, 替换成 ref 方法;
 * 2. 把 script 添加到 head
 * 3. 更新 script 后, 会初始化 window.$ref(), 通过 ref 代理对象触发 updateTemplate() 函数更新 dom
 */
export const setScript = ({ scriptStr }) => {
  if (!scriptStr) return;
  const regex = /import\s+{.*ref.*}\s+.*;/g;
  const str = scriptStr.replace(regex, "const ref = window.$ref;");
  const vScript = document.querySelector("head #app-script");
  if (vScript) {
    vScript.innerHTML = str;
  } else {
    const script = document.createElement("script");
    script.id = "app-script";
    script.innerHTML = str;
    document.querySelector("head").append(script);
  }
};

// 把组件中的 style 添加到 head
export const setStyle = async ({ styleStr }) => {
  const vStyle = document.querySelector("head #app-style");
  if (vStyle) {
    vStyle.innerHTML = styleStr;
  } else {
    const style = document.createElement("style");
    style.id = "app-style";
    style.innerHTML = styleStr;
    document.querySelector("head").append(style);
  }
};

// 1. 更新 template 模板中的内容; 2. 把组件中的 html 标签添加到 dom 挂载点
export const updateTemplate = async ({ templateStr, vm, proxyMap }) => {
  if (!templateStr) return;
  await nextTick();
  let str = templateStr;
  Object.entries(proxyMap).forEach(([key, value]) => {
    const reg = new RegExp(`{{ ${key} }}`, "g");
    str = str.replace(reg, value);
  });
  vm.innerHTML = str;
};

export const trigger = (key, value) => {
  console.log("trigger", key, value);
  APP_DATA.proxyMap[key] = value;
  return updateTemplate(APP_DATA);
};

const init = (param, vm) => {
  const data = compilerVue(param, vm);
  setScript(data); // 更新 script 后, 触发 updateTemplate() 函数更新 dom
  setStyle(data);
};

export default init;
